-- Copyright 2008 Jesse E. H-K
--
-- This file is part of Icepac.
--
-- Icepac is free software: you can redistribute it and/or modify
-- it under the terms of the GNU General Public License as published by
-- the Free Software Foundation, either version 3 of the License, or
-- (at your option) any later version.
--
-- Icepac is distributed in the hope that it will be useful,
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-- GNU General Public License for more details.
--
-- You should have received a copy of the GNU General Public License
-- along with Icepac.  If not, see <http://www.gnu.org/licenses/>.

require "libicepac"

-- BEGIN LOCAL FUNCTIONS --

-- Check if a table is empty.
local function empty(t)
    return t[1] == nil
end

-- Returns true when all the values in a table
-- are true.
local function all(t)
    for repo, found in pairs(t) do
        if not found then
            return false
        end
    end
    
    return true
end

-- Given a series of path "parts",
-- combine them with a path seperator.
local function buildpath(...)
    local result = {}
    for i = 1, arg.n do
        table.insert(result, arg[i])
    end
    
    return table.concat(result, "/")
end

local function exists(value, table)
    for k, v in pairs(table) do
        if v == value then
            return true
        end
    end
    
    return false
end

-- Searches for a file with a name matching
-- "file" in the directory "dir".
local function fileexists(file, dir)
    for ent in assert(icepac.iterdir(dir)) do
        if icepac.basename(ent) == file then
            return true
        end
    end
    
    return false
end

-- Escapes package names so that they can
-- be searched for correctly using patterns.
local function escape(str)
    return string.gsub(str, "-", "%%-")
end

-- Given a filepath, this function
-- will create the directory tree if it doesn't exist.
local function mkdirtree(filepath)
    local parts = { "/" }
    local cpath

    for part in string.gmatch(filepath, "[^\/]+") do
        table.insert(parts, part)
        cpath = table.concat(parts, "/")
        if not icepac.isdir(cpath) then
            assert(icepac.mkdir(cpath))
        end
    end
end

-- END LOCAL FUNCTIONS --

-- BEGIN EXPORTED FUNCTIONS --

-- Returns true if all repository entries specified in REPOS
-- are present in the specified ABS_ROOT
function icepac.checktree()
    local found = {}
    for _, repo in ipairs(REPOS) do
        found[repo] = false
    end
    
    for ent in assert(icepac.iterdir(ABS_ROOT)) do
        for _, repo in ipairs(REPOS) do
            if icepac.basename(ent) == repo then
                found[repo] = true
            end
        end
    end
    
    return all(found)
end

-- This function examines the contents of the given "dirname"
-- and determines if it is a package directory (that is,
-- determines if a PKGBUILD file is present).
function icepac.pkgdir(dirname)
    for ent in assert(icepac.iterdir(dirname)) do
        if icepac.basename(ent) == "PKGBUILD" then
            return true
        end
    end
    
    return false
end

-- Locate an package in the ABS tree
-- with an exact match to "name".
-- If a package is found, this function returns
-- the location of the package in the filesystem.
-- Otherwise, it returns nil.
function icepac.locate(name)
    local function find_r(current)
        local contents = assert(icepac.listdir(current))
        for _, ent in ipairs(contents) do
            if icepac.isdir(ent) then
                local base = icepac.basename(ent)
                if icepac.pkgdir(ent) then
                    if base == name then
                        return ent
                    end
                else
                    local result = find_r(buildpath(current, base))

                    if result then
                        return result
                    end
                end
            end
        end

        return nil
    end
    
    for repo in assert(icepac.iterdir(ABS_ROOT)) do
        if exists(icepac.basename(repo), REPOS) then
            local found = find_r(repo)

            if found then
                return found
            end
        end
    end

    return nil
end

-- Returns a table containing the paths to all
-- the packages matching the string "desired".
function icepac.search(desired)
    local foundpaths = {}
    local escapedpat = escape(desired)
    
    local function find_r(current)
        
        -- The use of icepac.listdir (which returns a table)
        -- over icepac.iterdir (which is an iterator) is important here.
        -- With the iterator, the system runs out of file handles
        -- due to the nature of this algorithm.
        local contents = assert(icepac.listdir(current))
        for _, ent in ipairs(contents) do
            if icepac.isdir(ent) then
                local b = icepac.basename(ent)
                
                if not string.find(LOCAL_DIR, b, 1, true) then
                    if icepac.pkgdir(ent) and string.match(b, escapedpat) then
                        table.insert(foundpaths, ent)
                    else
                        find_r(buildpath(current, b))
                    end
                end
            end
        end
    end
    
    for repo in assert(icepac.iterdir(ABS_ROOT)) do
        if exists(icepac.basename(repo), REPOS) then
            find_r(repo)
        end
    end
    
    if empty(foundpaths) then
        return nil
    else
        return foundpaths
    end
end

-- Copy a package with "name" and "loc" (location in the filesystem)
-- to the specified local directory. Will create the local
-- directory (and its hierarchy) if it doesn't already
-- exist.
function icepac.cppackage(name, loc)
    local floc = LOCAL_DIR .. "/" .. name
    
    if not icepac.isdir(LOCAL_DIR) then
        mkdirtree(LOCAL_DIR)
    end
    
    if not icepac.isdir(floc) then
        assert(icepac.mkdir(floc))
    end
    
    for ent in assert(icepac.iterdir(loc)) do  
        local base = icepac.basename(ent)
        assert(icepac.cpfile(ent, floc .. "/" .. base))
    end
    
    return floc
end

-- END EXPORTED FUNCTIONS --